#ifndef __SHM__HPP
#define __SHM_HPP

#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>

const std::string gpathname = "/root/linux-learning/Shm";
const int gproj_id = 0x66;
const int gShmSize = 4096;
const int gCreater = 1;
const int gUser = 2;

class Shm
{
private:
    key_t GetComKey()
    {
        key_t k = ftok(gpathname.c_str(), gproj_id);
        if (k < 0)
        {
            perror("ftok");
        }
        return k;
    }
    int GetShmHelper(key_t key, size_t size, int flag)
    {
        int shmid = shmget(key, size, flag);
        if (shmid < 0)
        {
            perror("shmget");
        }
        return shmid;
    }
    std::string RoleToString(int who)
    {
        if (who == gCreater)
            return "gCreater";
        else if (who == gUser)
            return "gUser";
        return "None";
    }
    // 挂接地址
    void *AttachShm()
    {
        // 当前地址不为空先分离地址
        if (_addrshm != nullptr)
        {
            DetachShm(_addrshm);
        }
        // 为空直接挂接地址
        void *addrshm = shmat(_shmid, nullptr, 0);
        if (addrshm == nullptr)
        {
            perror("AttachShm");
        }
        std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;
        return addrshm;
    }
    // 分离地址
    void DetachShm(void *shmaddr)
    {
        // 为空无需分离地址
        if (shmaddr == nullptr)
            return;
        shmdt(shmaddr);
        std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;
    }

public:
    Shm(const std::string &pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
    {
        _key = GetComKey(); // 初始化_key值
        if (_who == gCreater)
            GetShmUseCreate();
        else if (_who == gUser)
            GetShmForUser();

        std::cout << "shmid: " << _shmid << std::endl;
        std::cout << "_key: " << ToHex(_key) << std::endl;
        _addrshm = AttachShm(); // 挂接地址
    }
    std::string ToHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }
    bool GetShmUseCreate()
    {
        if (_who == gCreater)
        {
            // 不存在则创建，存在则返回错误
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid >= 0)
                return true;
            std::cout << "shm create done..." << std::endl;
        }
        return false;
    }
    bool GetShmForUser()
    {
        if (_who == gUser)
        {
            // 不存在则创建，存在则获取并返回
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
            // _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT);
            if (_shmid >= 0)
                return true;
            std::cout << "shm get done..." << std::endl;
        }
        return false;
    }
    // 获取地址
    void *Addr()
    {
        return _addrshm;
    }
    // 清理数据
    void Zero()
    {
        if (_addrshm)
        {
            memset(_addrshm, 0, gShmSize);
        }
    }
    void DebugShm()
    {
        struct shmid_ds ds;
        int n = shmctl(_shmid, IPC_STAT, &ds);
        if (n < 0)
            return;
        std::cout << "ds.shm_perm.__key : " << ToHex(ds.shm_perm.__key) << std::endl;
        std::cout << "ds.shm_nattch: " << ds.shm_nattch << std::endl;
    }

    ~Shm()
    {
        sleep(5);
        if (_who == gCreater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
            if (res < 0)
            {
                perror("shmctl");
            }
        }
        std::cout << "shm remove done..." << std::endl;
    }

private:
    key_t _key; // key值
    int _shmid; // 共享内存编号

    std::string _pathname; // 路径
    int _proj_id;

    int _who;       // 用户名
    void *_addrshm; // 内存地址
};

key_t GetComKey(const std::string &pathname, int proj_id)
{
    key_t k = ftok(pathname.c_str(), proj_id);
    if (k < 0)
    {
        perror("ftok");
    }
    return k;
}

int GetShm(key_t key, size_t size)
{
    // 不存在则创建，存在则返回错误
    // int shmid = shmget(key,size,IPC_CREAT | IPC_EXCL);
    // 不存在则创建，存在则获取并返回
    int shmid = shmget(key, size, IPC_CREAT);
    if (shmid < 0)
    {
        perror("shmget");
    }
    return shmid;
}

std::string ToHex(key_t key)
{
    char buffer[128];
    snprintf(buffer, sizeof(buffer), "0x%x", key);
    return buffer;
}

#endif